﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

using King.Extensions;
using King.Reflection;

using Antlr.Runtime;
using Antlr.Runtime.Tree;

namespace King.Language {

    public abstract class Ast {

        private Ast m_parent;
        private Token m_token;
        private List<Ast> m_children;

        protected Ast(IEnumerable<Ast> children = null, Token token = null) {
            m_token = token ?? Token.Empty;

            if (children != null) {
                m_children = children.WhereNotDefault().ToList();
                foreach (var child in m_children)
                    child.m_parent = this;
            }
        }

        public Ast Parent {
            get { return m_parent; }
        }
        public IEnumerable<Ast> Children() {
            return m_children ?? Enumerable.Empty<Ast>();
        }
        public Token Token {
            get { return m_token; }
        }

        public override string ToString() {
            string channelStr = "";
            string txt = Token.Text;
            if (txt != null) {
                txt = Regex.Replace(txt, "\n", "\\\\n");
                txt = Regex.Replace(txt, "\r", "\\\\r");
                txt = Regex.Replace(txt, "\t", "\\\\t");
            } else {
                txt = "<no text>";
            }
            return "[" + txt + ", <" + GetType().Name + ">" + channelStr + ", " + 
                Token.Line + ":" + Token.Column + "@" + Token.Position + "]";
        }
    }

    public sealed class Token {

        public static readonly Token Empty = new Token();

        private string m_text;
        private int m_line, m_column;
        private int m_postion, m_length;
        private int m_index;

        public Token(string text = null, int line = 0, int column = 0, int position = 0, int length = 0, int index = 0) {
            m_text = text;
            m_line = line;
            m_column = column;
            m_postion = position;
            m_length = length;
            m_index = index;
        }

        public string Text {
            get { return m_text; }
        }
        public int Line {
            get { return m_line; }
        }
        public int Column {
            get { return m_column; }
        }
        public int Position {
            get { return m_postion; }
        }
        public int Length {
            get { return m_length; }
        }
        public int Index {
            get { return m_index; }
        }
    }

    public sealed class Parser {

        //public static Parser Bug = new Parser(typeof(BugLexer), typeof(BugParser));

        private Type m_lexer;
        private Type m_parser;
        private Type m_ast;

        public Parser(Type lexer, Type parser, Type ast = null) {
            m_lexer = lexer;
            m_parser = parser;
            m_ast = ast;
        }

        public object Parse(string rule, string code) {

            // parse code and generate ast
            var stream = new ANTLRStringStream(code);
            var lexer = (ITokenSource)m_lexer.Activate(stream);
            var tokens = new CommonTokenStream(lexer);
            var parser = m_parser.Activate(tokens);
            var scope = (IAstRuleReturnScope)m_parser.Invoke(rule, parser);
            var tree = (CommonTree)scope.Tree;

            // rules should always return a value unless they are repeating rules
            // expose a null repeating rull as an empty enumeration
            if (tree == null)
                return Enumerable.Empty<CommonTree>();

            // verify ast
            if (m_ast != null) {
                var treeStream = new CommonTreeNodeStream(tree);
                var walker = m_ast.Activate(treeStream);
                m_ast.Invoke(rule, walker);
            }

            if (tree.IsNil)
                return tree.Children.Cast<CommonTree>();

            return tree;
        }
    }
}
